package net.esj.basic.plugins.hibernate.id;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import net.esj.basic.annotation.IdSerial;
import net.esj.basic.utils.DateUtils;
import net.esj.basic.utils.Validators;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.type.Type;

/**
 * 6位特殊标记位+8位机器编码+10位时间戳(精确到小时)+8位流水
 * @author Administrator
 *
 */
public class TimeSerialIdGenerator implements IdentifierGenerator, Configurable {

	private static final String DEFAULT_KEY_DEP = "TMSE";
	
	private static final String DEFAULT_KEY_RESOURCE = "ID";
	
	private String keyDep = DEFAULT_KEY_DEP;
	
	private String keyResource = DEFAULT_KEY_RESOURCE;
	
	private static Map<String,Sequenced> lastIdMap = new HashMap<String,Sequenced>();
	
	@Override
	public Serializable generate(SessionImplementor arg0, Object arg1)
			throws HibernateException {
		StringBuilder sb = new StringBuilder();
		String timePart = formatTime(DateUtils.now());
		Sequenced sequenced;
		synchronized(this){
			sequenced = lastIdMap.get(keyDep);
			if(sequenced==null){
				sequenced = new Sequenced(timePart);
				lastIdMap.put(keyDep, sequenced);
			}
		}
		synchronized(sequenced){
			if(sequenced.getTimePartToLong()<Long.valueOf(timePart)){
				sequenced.setSerial(0);
			}
			sb.append(keyDep).append(getAdapterAddress()).append(timePart).append(formateSerial(sequenced.increaseAndGetSerial()));
		}
		return sb.toString();
	}

	@Override
	public void configure(Type arg0, Properties arg1, Dialect arg2)
			throws MappingException {
		String className = arg1.getProperty("entity_name");
		try {
			Class c = Class.forName(className);
			IdSerial idSerial = (IdSerial) c.getAnnotation(IdSerial.class);
			if(idSerial!=null){
				keyDep = (idSerial.cate()+idSerial.resource());
				return;
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		
		keyDep = arg1.getProperty("target_table");
		if(!Validators.isEmpty(keyDep)){
			keyDep = generateKeyDep(keyDep);
		}else{
			keyDep = DEFAULT_KEY_DEP+DEFAULT_KEY_RESOURCE;
		}
	}
	
	/**
	 * 根据表名的分隔符'_'获得标记号
	 * 
	 * @param tableName
	 * @return
	 */
	private static final String generateKeyDep(String tableName){
		StringBuilder sb = new StringBuilder();
		String[] splits = tableName.split("_");
		int length = splits.length;
		int[] tmps = new int[6];
		for(int i=0;i<splits.length;i++){
			if(i<(splits.length-1)){
				sb.append(splits[i].charAt(0));
			}else{
				sb.append(splits[i]);
			}
		}
		while(sb.length()<6){
			sb.append("0");
		}
		if(sb.length()>6){
			return sb.substring(0, 6).toUpperCase();
		}
		return sb.toString().toUpperCase();
	}
	
	private static final NumberFormat nf = new DecimalFormat("00000000");
	
	private static String formateSerial(long serial){
		return nf.format(serial);
	}
	
	private static final DateFormat df = new SimpleDateFormat("yyyyMMddhh");

	private static String formatTime(Date date){
		return df.format(date);
	}
	
	private static String getAdapterAddress() {
		 byte addr[];
		try {
			addr = InetAddress.getLocalHost().getAddress();
		} catch (UnknownHostException e) {
			e.printStackTrace();
			addr = new byte[]{0,0,0,0};
		}
		 
		 StringBuffer sb = new StringBuffer();
		 
         for(byte b : addr){
        	 String s = Integer.toHexString(b & 0xFF);
 				sb.append(s.length() == 1 ? 0 + s : s);
         }
		// 把字符串所有小写字母改为大写成为正规的mac地址并返回
		return sb.toString().toUpperCase();
	}

	public static class Sequenced {
		private String timePart;

		private long serial = 0;
		
		public Sequenced(String timePart) {
			super();
			this.timePart = timePart;
		}

		public String getTimePart() {
			return timePart;
		}

		public long getTimePartToLong(){
			return Long.valueOf(timePart);
		}
		
		public void setTimePart(String timePart) {
			this.timePart = timePart;
		}

		public long getSerial() {
			return serial;
		}

		public void setSerial(long serial) {
			this.serial = serial;
		}
		public synchronized long increaseAndGetSerial(){
			this.serial = this.serial+1;
			return serial;
		}
	}
	
	public static void main(String[] args){
		final TimeSerialIdGenerator gen1 = new TimeSerialIdGenerator();
		for(int i=0;i<5;i++){
			Thread thread = new Thread(){
				@Override
				public void run() {
					while(true){
						String id = (String) gen1.generate(null,null);
						System.out.println(toString()+"------"+id);
						try {
							Thread.sleep(20L);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			};
			thread.start();
		}
		
	}
}
